/* (C) Robert de Bath 1997 under the terms of the GPL.
 *
 * This is a manual pager program, it will search for and format manual
 * pages which it then pipes to more.
 *
 * The program understands manual pages that have been compressed with
 * either 'compress' or 'gzip' and will decompress them on the fly.
 *
 * The environment is checked for these variables:
 *   MANSECT=1:2:3:4:5:6:7:8:9		# Manual section search order.
 *   MANPATH=/lib/man:/usr/man	# Directories to search for man tree.
 *   PAGER=more				# pager progam to use.
 *   PATH=...				# Search for gzip/uncompress
 *
 * The program will display documents that are either in it's own "nroff -man"
 * like format or in "catman" format, it will not correctly display pages in
 * the BSD '-mdoc' format.
 *
 * Neither nroff nor any similar program is needed as this program has it's
 * own built in _man_ _page_ _formatter_. This is NOT an nroff clone and will
 * not (for instance) understand macros or tbl constructs.
 *
 * Unlike groff this is small, fast and picky!
 */

#define DONT_SPLATTER /* Lots of messages out */

#include <stdio.h>
#ifdef __STDC__
#include <stdlib.h>
#include <unistd.h>
#endif
#include <ctype.h>
#include <string.h>
#include <limits.h>

#include <paths.h>
#ifdef _M_I86			/* ELKS */
#define MORE			"more"
#else					/* Host */
#define MORE			"less -R"
#endif

/* default .TH [extra1] value */
#define DEFAULT_EXTRA1	"ELKS Embeddable Linux Kernel Subset"

/* font translations to ANSI sequences (\fB -> bold, \fI -> underline) */
#define ANSI_NORMAL	 "\033[0m"	/* no attribute */
#define ANSI_BOLD	 "\033[1m"	/* bold */
#define ANSI_UNDERLINE   "\033[4m"	/* underline (normal on console for now) */
//#define ANSI_UNDERLINE "\033[7m"	/* reverse video */
//#define ANSI_UNDERLINE "\033[32m"	/* green */

FILE * ofd;
FILE * ifd;
int ifd_class = 0;		/* Type of ifd, 0=stdin, 1=file, 2=pipe */

char whitespace[256];
char word[80];			/* Current word */
int  no_nl=1;			/* Next NL in input file is ignored */
int  catmode = 1;		/* Have we seen a '.XX' command ? */

int  keep_nl = 0;		/* How many nl to keep til eof */
int  optional_keep=0;		/* Is the next keep optional ? */
int  pending_nl=0;		/* Is there a pending newline on output? */
int  no_fill = 0;		/* Disable 'filling' of lines */
int  right_adjust=1;		/* Adjust right margin */

int  standard_tab = 5;		/* Amount left margin stepped by */
int  left_indent = 0;		/* Current step of left margin */
int  old_para_indent=0; 	/* Indent to go back to after this paragraph */
int  next_line_indent=-1;	/* Indent after next line_break */
int  page_no = 1;		/* Page number */
int  input_tab = 8;		/* Tab width in input file */

int  right_margin = 65;		/* Don't print past this column */
int  page_length = 66;		/* Lines on page */
int  current_line = 0;		/* Line number = ? */
int  gaps_on_line = 0;		/* Gaps on line for adjustments */

int  *line_ptr = 0;
int  line[256];		/* Buffer for building output line */
int  cur_font = 0x100;	/* Current font, 1 == Roman */
int  old_font = 0x100;	/* Old font, 1 == Roman */

char line_header[256] = "";	/* Page header line */
char line_footer[256] = "";	/* Page footer line */
char little_header[256] = "";	/* Mini header for tty mode */

char man_file[PATH_MAX] = "";

int flg_w = 0;
int verbose = 1;


/* Prototypes */
void do_file(void);
void do_skipeol(void);
int do_fontwords(int this_font, int other_font, int early_exit);
void print_word(char *pword);
void line_break(void);
void page_break(void);
void print_header(void);
void print_footer(void);
void step(char **pcurr, char **pnext);
int do_command(void);
int do_argvcmd(int cmd_id);
int fetch_word(void);
int open_page(char * name);
int do_argvcmd(int cmd_id);
int do_noargs(int cmd_id);
void build_headers(void);

int find_page(char *name, char *sect)
{
static char defpath[] = _PATH_MANPAGES;
static char defsect[] = "1:2:3:4:5:6:7:8:9";
static char defsuff[] = ":.Z";
static char manorcat[] = "man:cat";

   char fbuf[PATH_MAX];
   char *manpath;
   char *mansect = sect;
   char *mansuff;
   char *mc, *mp, *ms, *su, *nmc, *nmp, *nms, *nsu;
   int rv = -1;

                  manpath = getenv("MANPATH");
   if (!manpath) manpath = defpath;
   if (!mansect) mansect = getenv("MANSECT");
   if (!mansect) mansect = defsect;
                  mansuff = defsuff;

   if (strchr(name, '/')) {
      for (su=nsu=mansuff,step(&su,&nsu); su; step(&su, &nsu)) {
         strcpy(fbuf, name);
	 strcat(fbuf, su);
         if ((rv=open_page(fbuf)) >= 0) break;
      }
      *man_file = 0;
      return rv;
   }

   /* SEARCH!! */
   for (mc=nmc=manorcat,step(&mc,&nmc); mc; step(&mc, &nmc))
      for (ms=nms=mansect,step(&ms,&nms); ms; step(&ms, &nms))
	 for (mp=nmp=manpath,step(&mp,&nmp); mp; step(&mp, &nmp))
	    for (su=nsu=mansuff,step(&su,&nsu); su; step(&su, &nsu)) {
	       /* max manpage name is 10, allows .1.Z for 14 MINIX max */
	       sprintf(fbuf, "%s/%s%s/%.10s.%s%s", mp, mc, ms, name, ms, su);
	       /* Got it ? */
	       if (access(fbuf, 0) < 0) continue;
	       if (flg_w)
	       {
		  printf("%s\n", fbuf);
		  rv = 0;
		  continue;
	       }

	       /* Try it ! */
	       if ((rv=open_page(fbuf)) >= 0)
	       {
		  char * p;
		  strcpy(man_file, fbuf);
		  p = strrchr(man_file, '/'); if (p) *p = 0;
		  p = strrchr(man_file, '/'); if (p) p[1] = 0;
		  return 0;
	       }
	    }

   return rv;
}

void step(char **pcurr, char **pnext)
{
   char *curr = *pcurr;
   char *next = *pnext;

   if (curr == 0) return;
   if (curr == next) {
      next = strchr(curr, ':');
      if (next) *next++ = 0;
   } else {
      curr=next;
      if (curr) {
         curr[-1] = ':';
         next = strchr(curr, ':');
         if (next) *next++ = 0;
      }
   }

   *pcurr = curr;
   *pnext = next;
}

int open_page(char * name)
{
   char *p, *command = 0;
   char buf[PATH_MAX];

   if (access(name, 0) < 0) return -1;

   p = strrchr(name, '.');
   if (p) {
      //if (strcmp(p, ".gz") == 0) command = "gzip -dc ";
      if (strcmp(p, ".Z") == 0)  command = "compress -dc ";
   }

   if (command) {
      strcpy(buf, command);
      strcat(buf, name);
      ifd = popen(buf, "r");
      if (ifd == 0) return -1;
      ifd_class = 2;
      return 0;
   }
   ifd = fopen(name, "r");
   if (ifd == 0) return -1;
   ifd_class = 1;
   return 0;
}

void close_page(void)
{
   switch (ifd_class)
   {
   case 1: fclose(ifd); break;
   case 2: pclose(ifd); break;
   }
   ifd_class = 0;
   return;
}

/****************************************************************************
 * Accepted nroff commands and executors.
 */

struct cmd_list_s {
   char cmd[3];
   int class;
   int id;
} cmd_list[] = {
  { "\\\"", 0, 0 },
  { "nh", 0, 0 },	/* This program never inserts hyphens */
  { "hy", 0, 0 },	/* This program never inserts hyphens */
  { "PD", 0, 0 },	/* Inter-para distance is 1 line */
  { "DT", 0, 0 },	/* Default tabs, they can't be non-default! */
  { "IX", 0, 0 },	/* Indexing for some weird package */
  { "Id", 0, 0 },	/* Line for RCS tokens */

  { "BY", 0, 0 },	/* I wonder where this should go ? */
  { "UC", 0, 0 },	/* University of California man page */
  { "in", 0, 0 },	/* ignore .in */
  { "ti", 0, 0 },	/* ignore .ti */

  { "nf", 0, 1 },	/* Line break, Turn line fill off */
  { "fi", 0, 2 },	/* Line break, Turn line fill on */
  { "sp", 0, 3 },	/* Line break, line space (arg for Nr lines) */
  { "br", 0, 4 },	/* Line break */
  { "bp", 0, 5 },	/* Page break */

  { "PP", 0, 6 }, { "LP", 0, 6 }, { "P", 0, 6 },  /* Paragraph */
  { "RS", 0, 7 },	/* New Para + Indent start */
  { "RE", 0, 8 },	/* New Para + Indent end */
  { "HP", 0, 9 },	/* Begin hanging indent (TP without arg?) */

  { "ad", 0, 10 },	/* Line up right margin */
  { "na", 0, 11 },	/* Leave right margin unaligned */
  { "ta", 0, 12 },	/* Changes _input_ tab spacing, right? */

  { "TH", 1, 1 },	/* Title and headers */
  { "SH", 1, 2 },	/* Section */
  { "SS", 1, 3 },	/* Subsection */
  { "IP", 1, 4 },	/* New para, indent except argument 1 */
  { "TP", 1, 5 },	/* New para, indent except line 1 */
  { "ft", 1, 6 },	/* Font {R,B,I,P} */

  { "B",  2, 22 },	/* Various font fiddles */
  { "BI", 2, 23 },
  { "BR", 2, 21 },
  { "I",  2, 33 },
  { "IB", 2, 32 },
  { "IR", 2, 31 },
  { "RB", 2, 12 },
  { "RI", 2, 13 },
  { "SB", 2, 42 },
  { "SM", 2, 44 },

  { "C",  2, 22 },	/* PH-UX manual pages! */
  { "CI", 2, 23 },
  { "CR", 2, 21 },
  { "IC", 2, 32 },
  { "RC", 2, 12 },

  { "so", 3, 0 },

  { "\0\0", 0 }
};

/****************************************************************************
 * ifd is the manual page, ofd is the 'output' file or pipe, format it!
 */
void do_file(void)
{
   int nl;
   ungetc('\r', ifd);

   while ((nl=fetch_word())>=0)
   {
#ifdef SPLATTER
      fprintf(ofd, ">WS='%s',", whitespace);
      fprintf(ofd, "catmode=%d,", catmode);
      fprintf(ofd, "nl=%d,", nl);
      fprintf(ofd, "no_nl=%d,", no_nl);
      fprintf(ofd, "no_fill=%d,", no_fill);
      fprintf(ofd, "keep_nl=%d,", keep_nl);
      fprintf(ofd, "opt_keep=%d,", optional_keep);
      fprintf(ofd, "WD='%s',", word);
      fprintf(ofd, "\n");
#endif

      if (catmode)
      {
	 if (strcmp(word, "'\\\"") == 0 || strcmp(word, "'''") == 0)
	 {
	    /* This is a marker sometimes used for opening subprocesses like
	     * tbl and equ; this program ignores it.
	     */
	    do_skipeol();
	 }
	 else if (*whitespace == '\r')
            fprintf(ofd, "%s%s", whitespace+1, word);
	 else
            fprintf(ofd, "%s%s", whitespace, word);
      }
      else
      {
         if (keep_nl && nl && !no_nl)
	 {
	    if (optional_keep)
	    {
	       optional_keep = 0;
	       if (line_ptr == 0 || next_line_indent < 0 ||
	           left_indent + (line_ptr-line) + 1 > next_line_indent)
	          line_break();
	       else if (line_ptr != 0 && next_line_indent > 0)
	       {
	           while (left_indent + (line_ptr-line) + 1 <= next_line_indent)
		      *line_ptr++ = cur_font + ' ';
	       }
	    }
	    else line_break();
	    if (keep_nl>0) keep_nl--;
	 }

         if (nl == 1 && ( word[0] == '.' ||
	                  (word[0] == '\'' && strcmp(word, "'\\\"") == 0) ||
	                  (word[0] == '\'' && strcmp(word, "'''") == 0)
	  ))
         {
	    no_nl=1;
            if (do_command() < 0) break;
         }
         else
         {
	    if (nl == 1 && no_fill)
	       line_break();
	    if (*whitespace)
               print_word(whitespace);
            print_word(word);
	    no_nl=0;
         }
      }
   }

   page_break();
}

int fetch_word(void)
{
   static int col = 0;
   char * p;
   int ch, nl;

   nl = 0;
   *(p = whitespace) = 0;

   if (!catmode && !no_fill) p++;

   while ((ch=fgetc(ifd)) != EOF && isspace(ch)) {
      if (nl && no_fill && ch != '.' && ch != '\n') break;
      if (nl && !catmode && ch == '\n') {
         *whitespace=0;
	 strcpy(word, ".sp");
         ungetc(ch, ifd);
	 return 1;
      }
      nl = (ch == '\n' || ch == '\r');
      if (nl) col=0; else col++;

      if (no_fill && nl && *whitespace) {
         *word=0; ungetc(ch, ifd); return 0;
      }

      if (p<whitespace+sizeof(whitespace)-1 && (!nl || catmode))
         *p++ = ch;

      if (ch == '\t' && !catmode) {
	 p[-1] = ' ';
         while (col % input_tab) {
            if (p<whitespace+sizeof(whitespace)-1)
               *p++ = ' ';
	    col++;
	 }
      }

      if (!catmode && !no_fill && nl) *(p = whitespace) = 0;
   }
   *p=0;

   if (catmode && ch == '.' && nl) catmode = 0;

   *(p = word) = 0;
   if (ch == EOF || p > word+sizeof(word)/2) {
      if (p!=word) {
         ungetc(ch, ifd);
	 *p = 0;
	 return nl;
      }
      return -1;
   }
   ungetc(ch, ifd);

   while ((ch=fgetc(ifd)) != EOF && !isspace(ch)) {
      if (p<word+sizeof(word)-1) *p++ = ch, col++;
      if (ch == '\\') {
         if ((ch=fgetc(ifd)) == EOF) break;
	 /* if (ch == ' ') ch = ' ' + 0x80;*/	/* XXX Is this line needed? */
         if (p<word+sizeof(word)-1) *p++ = ch, col++;
      }
   }
   *p = 0;
   ungetc(ch, ifd);

   return (nl != 0);
}

int do_command(void)
{
   char * cmd;
   int i;
   char lbuf[10];

   cmd = word + 1;

   /* Comments don't need the space */
   if (strncmp(cmd, "\\\"", 2) == 0) cmd="\\\"";

   for (i=0; cmd_list[i].cmd[0]; i++) {
      if (strcmp(cmd_list[i].cmd, cmd) == 0) break;
   }

   if (cmd_list[i].cmd[0] == 0) {
      if (verbose) {
         strncpy(lbuf, cmd, 3); lbuf[3] = 0;
         //line_break();
         //i=left_indent; left_indent=0;
         //strcpy(word, "**** Unknown formatter command: .");
         strcpy(word, " .");
         strcat(word, lbuf);
	 strcat(word, " ");
         print_word(word);
         //line_break();
         //left_indent=i;
      }

      i=0;	/* Treat as comment */
   }

   switch (cmd_list[i].class) {
   case 1: /* Parametered commands */
      return do_argvcmd(cmd_list[i].id);

   case 2: /* Font changers */
      return do_fontwords(cmd_list[i].id/10, cmd_list[i].id%10, 0);

   case 3: /* .so */
      fetch_word();
      strcat(man_file, word);
      close_page();
      if (find_page(man_file, (char*)0) < 0) {
         fprintf(stderr, "Cannot open .so file %s\n", word);
	 return -1;
      }
      ungetc('\r', ifd);
      break;

   default:
      do_skipeol();
      if (cmd_list[i].id) return do_noargs(cmd_list[i].id);
   }
   return 0;
}

void do_skipeol(void)
{
   int ch;
   char * p = word;

   while ((ch=fgetc(ifd)) != EOF && ch != '\n')
      if (p<word+sizeof(word)-1) *p++ = ch;
   *p = 0;
   ungetc(ch, ifd);
   return;
}

int do_fontwords(int this_font, int other_font, int early_exit)
{
   static char ftab[] = " RBIS";
   char *p=word;
   int i, ch;
   int in_quote = 0;

   no_nl=0;	/* Line is effectivly been reprocessed so NL is visable */
   for (;;)
   {
      if (p == word) {
         strcpy(p, "\\f"); p[2]=ftab[this_font]; p+=3;
      }
      if ((ch=fgetc(ifd)) == EOF || ch == '\n') break;
      if (ch == '"') {
         in_quote = !in_quote;
	 continue;
      }
      if (in_quote || !isspace(ch)) {
	 if (isspace(ch) && p > word+3) {
            strcpy(p, "\\fR"); p+=3; *p=0; print_word(word);
	    p=word;
	    if (no_fill) print_word(" ");
	    continue;
         }
         if (p<word+sizeof(word)-4) *p++ = ch;
	 if (ch == '\\') {
            if ((ch=fgetc(ifd)) == EOF || ch == '\n') break;
            if (p<word+sizeof(word)-4) *p++ = ch;
	 }
	 continue;
      }

      if (p != word+3) {
         if (early_exit) break;

	 if (this_font == other_font)
	 {
            strcpy(p, "\\fR"); p+=3; *p=0; print_word(word);
	    p=word;
	 }
         i=this_font; this_font=other_font; other_font=i;
         if (p<word+sizeof(word)-4) {
            strcpy(p, "\\f"); p[2]=ftab[this_font]; p+=3;
         }
      }
   }
   ungetc(ch, ifd);

   if (p > word+3) {
      strcpy(p, "\\fR"); p+=3;
      *p=0;
      print_word(word);
   }

   return 0;
}

int do_noargs(int cmd_id)
{
   if (cmd_id < 10) line_break();
   switch (cmd_id) {
   case 1: no_fill = 1; break;			/* .nf */
   case 2: no_fill = 0; break;			/* .fi */
   case 3: pending_nl = 1; break;		/* .sp */
   case 4: break;				/* .br */
   case 5: page_break(); break;			/* .bp */
   case 6: left_indent = old_para_indent;	/* .PP */
           pending_nl = 1;
	   break;
   case 7: pending_nl = 1;			/* .RS */
           left_indent += standard_tab;
           old_para_indent += standard_tab;
	   break;
   case 8: pending_nl = 1;			/* .RE */
           left_indent     -= standard_tab;
           old_para_indent -= standard_tab;
	   break;
   case 9: pending_nl = 1;			/* .HP */
           next_line_indent = old_para_indent + standard_tab;
	   left_indent = old_para_indent;
	   break;

   case 10: right_adjust=1; break;		/* .ad */
   case 11: right_adjust=0; break;		/* .na */
   case 12: input_tab=atoi(word);		/* .ta */
            if (input_tab<=0) input_tab=8;
	    break;
   }
   return 0;
}

int do_argvcmd(int cmd_id)
{
   int ch;

   line_break();
   while ( (ch=fgetc(ifd)) != EOF && (ch==' ' || ch=='\t'))
      ;
   ungetc(ch, ifd);

   switch (cmd_id + 10*(ch=='\n')) {
   case 1:	/* .TH Title and headers */
      page_break();
      left_indent = old_para_indent = standard_tab;
      build_headers();
      break;

   case 2:	/* .SH Section */
      left_indent = 0;
      next_line_indent = old_para_indent = standard_tab;
      no_nl = 0; keep_nl = 1; pending_nl=1;

      do_fontwords(1,1,0);
      return 0;
   case 3:	/* .SS Subsection */
      left_indent = standard_tab/2;
      next_line_indent = old_para_indent = standard_tab;
      no_nl = 0; keep_nl = 1; pending_nl=1;

      do_fontwords(1,1,0);
      break;

   case 5:	/* .TP New para, indent except line 1 */
   case 15:	/* .TP no argument */
      do_skipeol();
      next_line_indent = old_para_indent + standard_tab;
      left_indent = old_para_indent;
      pending_nl=1;
      keep_nl = 1;
      optional_keep = 1;
      break;

   case 4:	/* .IP New para, indent except argument 1 */
      next_line_indent = old_para_indent + standard_tab;
      left_indent = old_para_indent;
      pending_nl=1;
      keep_nl = 1;
      optional_keep = 1;
      do_fontwords(1,1,1);
      do_skipeol();
      break;

   case 14:	/* .IP no argument */
      pending_nl = 1;
      left_indent = old_para_indent + standard_tab;
      break;

   case 6:	/* .ft {R,B,I,P} */
      do_skipeol();
      switch (word[0]) {
      case 'B': old_font = cur_font; print_word("\\fB"); break;
      case 'I': old_font = cur_font; print_word("\\fI"); break;
      case 'R': old_font = cur_font; print_word("\\fR"); break;
      case 'P': cur_font = old_font; print_word("\\fP"); break;
      }
      break;
   }

   return 0;
}

void build_headers(void)
{
   char buffer[5][80];
   int  strno=0, stroff=0;
   int  last_ch = 0, ch, in_quote=0;

   for (ch=0; ch<5; ch++)
      buffer[ch][0] = 0;

   for (;;)
   {
      if ((ch=fgetc(ifd)) == EOF || ch == '\n') break;
      if (ch == '"')
      {
	 if (last_ch == '\\') { stroff--; break; }
         in_quote = !in_quote;
	 continue;
      }
      last_ch = ch;
      if (in_quote || !isspace(ch))
      {
	 /* Nb, this does nothing about backslashes, perhaps it should */
         if (stroff< sizeof(buffer[strno])-1)
            buffer[strno][stroff++] = ch;
	 continue;
      }
      buffer[strno][stroff] = 0;

      if (buffer[strno][0])
      {
         strno++;
	 stroff=0;
	 if (strno == 5) break;
      }
   }
   if (strno < 5)
      buffer[strno][stroff] = 0;
   ungetc(ch, ifd);

   /* Ok we should have upto 5 arguments build the header and footer */

   if (buffer[2][0] == 0)
      strcpy(buffer[2], DEFAULT_EXTRA1);	/* header middle */

   memset(little_header, ' ', right_margin);

   memset(line_header, ' ', right_margin);
   strcpy(line_header, buffer[0]);
   strcat(line_header, "(");
   strcat(line_header, buffer[1]);
   strcat(line_header, ")");
   ch = strlen(line_header);
   strcpy(line_header+right_margin-ch, line_header);
   line_header[ch] = ' ';

   ch = strlen(buffer[4]);
   if (ch > right_margin) ch = right_margin-12;
   memcpy(line_header+right_margin/2-ch/2, buffer[4], ch);

   //memcpy(little_header, line_header, right_margin/2+ch/2+1);
   memcpy(little_header, line_header, right_margin);
   //strcpy(little_header+right_margin-strlen(buffer[2]), buffer[2]);

   memset(line_footer, ' ', right_margin-6);
   line_footer[right_margin-6] = 0;
   memcpy(line_footer, buffer[3], strlen(buffer[3]));

   ch = strlen(buffer[2]);
   if (ch > right_margin) ch = right_margin-12;
   memcpy(line_footer+right_margin/2-ch/2, buffer[2], ch);
   memcpy(little_header+right_margin/2-ch/2, buffer[2], ch);

   do_skipeol();
}

void print_word(char *pword)
{
/* Eat   \&  \a .. \z and \A .. \Z
 * \fX   Switch to font X (R B I S etc)
 * \(XX  Special character XX
 * \X    Print as X
 */

   char *s;
   int *d;
   int length=0;
   int wword[256];
   int sp_font = cur_font;

   /* Eat and translate characters. */
   for (s=pword,d=wword; *s; s++)
   {
      if (*s == '\n') continue;
      if (*s != '\\') { *d++ = *s + cur_font; length++; }
      else
      {
         if (s[1] == 0) break;
	 s++;
	 if (*s == 'f')
	 {
	    if (s[1]) {
	       static char fnt[] = " RBIP";
	       char * p = strchr(fnt, *++s);
	       if (p == 0) cur_font = 0x100;
	       else         cur_font = 0x100*(p-fnt);
		   if (cur_font == 0x400) cur_font = sp_font;
	    }
	    continue;
	 }
	 else if (*s == 's')
	 {
	    /* Font size adjusts - strip */
	    while (s[1] && strchr("+-0123456789", s[1])) s++;
	    continue;
	 }
	 else if (isalpha(*s) || strchr("!&^[]|~", *s)) continue;
	 else if (*s == '(' || *s == '*')
	 {
	    /* XXX Humm character xlate */

	    if (*s == '*')
		if (s[1]) ++s;
	    if (s[1]) ++s;
	    if (s[1]) ++s;
	    *d++ = '*' + cur_font;
	    length++;
	    continue;
	 }

	 *d++ = *s + cur_font;
	 length++;
      }
   }

   *d=0;
#ifdef SPLATTER
   {
      int *x;
      fprintf(ofd, ">WORD:");
      for (x=wword; *x; x++) fputc(*x, ofd);
      fprintf(ofd, ":\n");
   }
#endif

   if (*wword == 0) return;

   if (line_ptr)
      if (line_ptr + ((line_ptr[-1]&0xFF) == '.') - line + length
            >= right_margin - left_indent)
      {
         right_adjust = -right_adjust;
         line_break();
      }

   if (line_ptr == 0) line_ptr = line;
   else if (!no_fill && (line_ptr[-1]&0xFF) > ' ')
   {
      if ((line_ptr[-1]&0xFF) == '.')
         *line_ptr++ = cur_font + ' ';
      *line_ptr++ = sp_font;
      gaps_on_line++;
   }

   memcpy(line_ptr, wword, length*sizeof(int));
   line_ptr += length;
}

void line_break(void)
{
   int * d, ch;
   int spg=1, rspg=1, spgs=0, gap=0;

   if (line_ptr == 0) return ;

   if (current_line == 0) print_header();

   if (page_length > 12 &&
      current_line+pending_nl > page_length-6)
   {
      print_footer();
      print_header();
   }

   if (current_line) current_line += 1+pending_nl;
   for (;pending_nl>0; pending_nl--)
      fprintf(ofd, "\n");

   if (right_adjust<0)
   {
      int over = right_margin-left_indent-(line_ptr-line);
#ifdef SPLATTER
      fprintf(ofd, ">Gaps=%d, Over=%d, ", gaps_on_line, over);
#endif
      if (gaps_on_line && over) {
         spg=rspg= 1+over/gaps_on_line;
	 over = over%gaps_on_line;
	 if (over) {
	    if (current_line % 2) {
	       spgs = over;
	       spg++;
	    } else {
	       spgs = gaps_on_line-over;
	       rspg++;
	    }
	 }
      }
#ifdef SPLATTER
      fprintf(ofd, " (%d,%d) sw=%d\n", spg, rspg, spgs);
#endif
      right_adjust=1;
   }

   *line_ptr = 0;
   if (*line)
      for (ch=left_indent; ch>0; ch--)
         fputc(' ', ofd);

   for (d=line; *d; d++) {
      ch = *d;
      if ((ch & 0xFF) == 0) {
         int i;
         if (gap++ < spgs) i=spg; else i=rspg;
	 for (;i>0;i--)
	    fputc(' ', ofd);
      }
      else switch (ch >> 8) {
      case 2:	/* \fB (bold) -> ANSI bold */
         /*fputc(ch&0xFF, ofd); fputc('\b', ofd); fputc(ch&0xFF, ofd); break;*/
         fputs(ANSI_BOLD, ofd); fputc(ch&0xFF, ofd); fputs(ANSI_NORMAL, ofd); break;
      case 3:	/* \fI (italic) -> ANSI underline */
         /*fputc('_', ofd); fputc('\b', ofd); fputc(ch&0xFF, ofd); break;*/
         fputs(ANSI_UNDERLINE, ofd); fputc(ch&0xFF, ofd); fputs(ANSI_NORMAL, ofd); break;
      case 1:	/* roman -> no action */
      default:
         fputc(ch&0xFF, ofd); break;
      }
   }
   fputc('\n', ofd);

   line_ptr = 0;

   if (next_line_indent > 0)
      left_indent = next_line_indent;
   next_line_indent = -1;
   gaps_on_line=0;
}

void page_break(void)
{
   line_break();
   if (current_line)
      print_footer();
}

void print_header(void)
{
   pending_nl = 0;

   if (*line_header && page_length)
   {
      current_line = 7;
      fprintf(ofd, "\n\n\n%s\n\n\n", line_header);
   }
   else if (*little_header && !page_length)
   {
      current_line = 1;
      fprintf(ofd, "%s\n\n", little_header);
   }
}

void print_footer(void)
{
   if (!page_length) return;

   while (current_line <= page_length-3)
   {
      fputc('\n', ofd);
      current_line++;
   }

   fprintf(ofd, "%s%6d\n\n\n", line_footer, page_no++);
   current_line = 0;
}

/****************************************************************************
 * Main routine, hunt down the manpage.
 */
int main(int argc, char **argv)
{
   int do_pclose_ofd = 0;
   int ar;
   char * mansect = 0;
   char * manname = 0;

   ifd = stdin;
   ofd = stdout;
   for (ar=1; ar<argc; ar++) if (argv[ar][0] == '-') {
      char * p;
      for (p=argv[ar]+1; *p; p++) switch (*p) {
	      case 'w': flg_w=1; break;
	      case 'v': verbose=1; break;
	      case 'q': verbose=0; break;
      }
   }
   else if (isdigit(argv[ar][0]) 
		&& !argv[ar][1]) mansect = argv[ar]; /* man page names may start with a number */
   else if (manname == 0)         manname = argv[ar];
   else if (mansect == 0) {
      mansect = manname;
      manname = argv[ar];
   } else {
      fprintf(stderr, "Ignoring argument %s\n", argv[ar]);
      break;
   }

   if (manname == 0) {
      fprintf(stderr, "Which manpage ?\n");
      exit(1);
   }

   if (find_page(manname, mansect) < 0) {
      if (mansect > 0)
         fprintf(stderr, "No entry for %s in section %s of the manual.\n",
	                  manname, mansect);
      else
         fprintf(stderr, "No manual entry for %s\n", manname);
      exit(1);
   }
   if (flg_w) exit(0);

   /* ifd is now the file - display it */
   if (isatty(1)) {	/* If writing to a tty do it to a pager */
	  char *pager = getenv("PAGER");
      ofd = pager? popen(pager, "w"): 0;
      if (ofd == 0) ofd = popen(MORE, "w");
      if (ofd == 0) {
         ofd = stdout;
      } else {
         do_pclose_ofd=1;
	 page_length = 0; right_margin=78;
      }
   }

   do_file();

   /* Close files */
   if (do_pclose_ofd) pclose(ofd);
   close_page();
   exit(0);
}
